#ifndef _WIN32
#include <stdlib.h>
#endif
#include <iostream>
#include "Client.h"
#include "Buffer.h"
#include "Mutex.h"
#include "Event.h"

using namespace AgoraClient;

Client::Client(IBuffer *bufferSound, IBuffer *bufferTxtAdm, unsigned int bufferReadSize) :
_bufferSound(bufferSound),
_bufferTxtAdm(bufferTxtAdm),
_bufferReadSize(bufferReadSize),
_id(-1),
_availableToSendSound(0),
_availableToSendTxtAdm(0),
_sendLoop(true),
_isConnected(false)
{
	this->_buffSendSound = new Buffer(20000);
	this->_lockSendSound = new Mutex();
	this->_eventBufferSound = new Event();
	this->_eventDestructorSound = new Event();
	this->_buffSendTxtAdm = new Buffer(20000);
	this->_lockSendTxtAdm = new Mutex();
	this->_eventBufferTxtAdm = new Event();
	this->_eventDestructorTxtAdm = new Event();

	ThreadPool::Request *rqt = new ThreadPool::Request();
	rqt->function = &Client::SendDataSound;
	rqt->autoFree = true;
	rqt->parameters = this;
	ThreadPool::GetThreadPool()->AddRequest(rqt);

	rqt = new ThreadPool::Request();
	rqt->function = &Client::SendDataTxtAdm;
	rqt->autoFree = true;
	rqt->parameters = this;
	ThreadPool::GetThreadPool()->AddRequest(rqt);
}

Client::~Client()
{
	this->_sendLoop = false;
	this->_eventBufferSound->Set();
	this->_eventDestructorSound->Wait();
	this->_eventBufferTxtAdm->Set();
	this->_eventDestructorTxtAdm->Wait();
	delete this->_buffSendSound;
	delete this->_lockSendSound;
	delete this->_eventBufferSound;
	delete this->_eventDestructorSound;
	delete this->_buffSendTxtAdm;
	delete this->_lockSendTxtAdm;
	delete this->_eventBufferTxtAdm;
	delete this->_eventDestructorTxtAdm;
	delete _connectionSound;
}

int Client::Connect(const std::string& adress, int portTxtAdm, int portSound, const std::string& login, const std::string& pass)
{
	unsigned int size(sizeof(int));

	this->_connectionTxtAdm = new Guest();
	if (this->_connectionTxtAdm->Init())
		return (-1);
	this->_connectionSound = new Guest();
	if (this->_connectionSound->Init())
		return (-1);
	this->_connectionSound->Connect(adress, portSound);
	this->_connectionTxtAdm->Connect(adress, portTxtAdm);
	if (this->Authentificate(login, pass) >= 0)
	{
		ThreadPool::Request *rqt = new ThreadPool::Request();
		rqt->function = &Client::SelectReceiveSound;
		rqt->autoFree = true;
		rqt->parameters = this;
		ThreadPool::GetThreadPool()->AddRequest(rqt);
		rqt = new ThreadPool::Request();
		rqt->function = &Client::SelectReceiveTxtAdm;
		rqt->autoFree = true;
		rqt->parameters = this;
		ThreadPool::GetThreadPool()->AddRequest(rqt);
		this->_isConnected = true;
	}
	else
	{
		this->Disconnect();
		return (-1);
	}
	return (0);
}

void		Client::SelectReceiveTxtAdm(void* data)
{
	unsigned int len(0);
	unsigned int available(0);
	Client *client = reinterpret_cast<Client*>(data);
	char *buff = new char[client->_bufferReadSize];
	

	Client::SelectReceive(client->_connectionTxtAdm, client->_bufferTxtAdm, client->_bufferReadSize, len, available, buff);
	//client->Disconnect();
	delete buff;
	client->Disconnect();
}

void	Client::SelectReceiveSound(void* data)
{
	unsigned int len(0);
	unsigned int available(0);
	Client *client = reinterpret_cast<Client*>(data);
	char *buff = new char[client->_bufferReadSize];


	Client::SelectReceive(client->_connectionSound, client->_bufferSound, client->_bufferReadSize, len, available, buff);
	//client->Disconnect();
	delete buff;
	client->Disconnect();
}

void Client::SelectReceive(Guest* connection, IBuffer *buffer, unsigned int bufferReadSize, unsigned int len, unsigned int available, char *buff)
{
	while(1)
	{
		available = buffer->SizeMax() - buffer->Size();

		len = available > bufferReadSize ? bufferReadSize : available;
		if (connection->ReceiveData(buff, &len, 0) == 0)
		{
			if (len > 0)
			{
					buffer->Write(buff, len);
					if (buffer->Size() > 50000)
					{
						exit(0);
					}
			}
			else
			{
				break;
			}
		}
		else
		{
			break;
		}
	}
}

bool			Client::IsConnected()
{
	return (this->_isConnected);
}

int Client::Disconnect()
{
	this->_isConnected = false;
	_bufferSound->Clean();
	_bufferTxtAdm->Clean();
	_id = -1;
	_availableToSendSound = 0;
	_availableToSendTxtAdm = 0;
	return (this->_connectionSound->CloseSocket() || this->_connectionTxtAdm->CloseSocket());
}

// TODO archi ? voir comment mettre l'authentif dans l'admin et non dans le network
int	Client::Authentificate(const std::string& login, const std::string& pass)
{
	char	buff[sizeof(int)];
	unsigned int res;
	unsigned int sizedp;
	unsigned int sizestr;
	timeval tm;

	tm.tv_sec = 5;
	tm.tv_usec = 0;

	sizedp = sizeof(int);
	// pas safe mais passable
	res = this->_connectionTxtAdm->ReceiveData(buff, &sizedp, &tm);
	if (res)
		return (-1);
	if (sizedp == sizeof(int))
	{
		sizestr = login.length() + 1;
		
		if (this->_connectionTxtAdm->SendData(&sizestr, &sizedp, &tm) ||
			this->_connectionTxtAdm->SendData((void*)login.c_str(), &sizestr, &tm))
			return -1;
		sizestr = pass.length() + 1;
		if (this->_connectionTxtAdm->SendData(&sizestr, &sizedp, &tm) ||
			this->_connectionTxtAdm->SendData((void*)pass.c_str(), &sizestr, &tm))
			return -1;
				///DEBUG
		char validco = '0';
		sizestr = 1;
		if (this->_connectionTxtAdm->ReceiveData(&validco, &sizestr, &tm) || validco == '0')
			return -1;

		// TODO identification port son
		if (this->_connectionSound->SendData(buff, &sizedp, &tm))
			return -1;
		return (this->_id = (int)*buff);
	}
	
	return (-1);
}

int	Client::GetId()
{
	return (this->_id);
}


//-------------------------------------J
unsigned int	Client::SendBufferSound(void* data, unsigned int size)
{
	this->_lockSendSound->Lock();
	if (this->_buffSendSound->SizeMax() - this->_buffSendSound->Size() < size)
	{
		this->_lockSendSound->Unlock();
		return (-1);
	}
	if (this->_buffSendSound->Write(data, size) >= 0)
	{
		this->_availableToSendSound += size;
		this->_eventBufferSound->Set();
	}
	this->_lockSendSound->Unlock();
	return (0);
}

void	Client::SendDataSound(void* client)
{
	Client*			clt = reinterpret_cast<Client*>(client);
	unsigned int	size;
	unsigned int	sentSize;
	void*			data;
	timeval			tm;


	while (clt->_sendLoop)
	{
		clt->_eventBufferSound->Wait();
		clt->_lockSendSound->Lock();
		size = clt->_availableToSendSound;
		clt->_lockSendSound->Unlock();
		data = clt->_buffSendSound->Read(size, false);
		tm.tv_sec = 1;
		tm.tv_usec = 0;
		sentSize = size;
		if (clt->_connectionSound->SendData(data, &sentSize, &tm))
			clt->Disconnect();
		
		clt->_lockSendSound->Lock();
		if (sentSize == clt->_availableToSendSound)
		{
			clt->_eventBufferSound->Reset();
		}
		clt->_availableToSendSound -= sentSize;
		clt->_buffSendSound->Delete(sentSize);
		clt->_lockSendSound->Unlock();
	}
	clt->_eventDestructorSound->Set();
}


unsigned int	Client::SendBufferTxtAdm(void* data, unsigned int size)
{
	this->_lockSendTxtAdm->Lock();
	if (this->_buffSendTxtAdm->SizeMax() - this->_buffSendTxtAdm->Size() < size)
	{
		this->_lockSendTxtAdm->Unlock();
		return (-1);
	}
	if (this->_buffSendTxtAdm->Write(data, size) >= 0)
	{
		this->_availableToSendTxtAdm += size;
		this->_eventBufferTxtAdm->Set();
	}
	this->_lockSendTxtAdm->Unlock();
	return (0);
}

void	Client::SendDataTxtAdm(void* client)
{
	Client*			clt = reinterpret_cast<Client*>(client);
	unsigned int	size;
	unsigned int	sentSize;
	void*			data;
	timeval			tm;


	while (clt->_sendLoop)
	{
		clt->_eventBufferTxtAdm->Wait();
		clt->_lockSendTxtAdm->Lock();
		size = clt->_availableToSendTxtAdm;
		clt->_lockSendTxtAdm->Unlock();
		data = clt->_buffSendTxtAdm->Read(size, false);
		tm.tv_sec = 1;
		tm.tv_usec = 0;
		sentSize = size;
		if (clt->_connectionTxtAdm->SendData(data, &sentSize, &tm))
			clt->Disconnect();
		
		clt->_lockSendTxtAdm->Lock();
		if (sentSize == clt->_availableToSendTxtAdm)
		{
			clt->_eventBufferTxtAdm->Reset();
		}
		clt->_availableToSendTxtAdm -= sentSize;
		clt->_buffSendTxtAdm->Delete(sentSize);
		clt->_lockSendTxtAdm->Unlock();
	}
	clt->_eventDestructorTxtAdm->Set();
}

//-------------------------------------J
